package com.zmzncs.nat.channel;

import com.alibaba.fastjson.JSONObject;
import com.zmzncs.nat.model.InteractiveModel;
import com.zmzncs.nat.model.SecretInteractiveModel;
import com.zmzncs.nat.utils.AESUtil;
import lombok.*;
import lombok.EqualsAndHashCode.Exclude;
import lombok.extern.slf4j.Slf4j;

import java.io.IOException;
import java.net.Socket;
import java.nio.charset.Charset;
import java.security.Key;

/**
 * 
 * <p>
 * InteractiveModel 加密型通道，AES加密
 * </p>
 *
 * @author Pluto
 * @since 2020-01-08 16:16:12
 */
@Data
@EqualsAndHashCode(callSuper = false)
@Slf4j
public class SecretInteractiveChannel extends SocketChannel<InteractiveModel, InteractiveModel> {

    @Exclude
    @Setter(AccessLevel.NONE)
    @Getter(AccessLevel.NONE)
    private JsonChannel channle;

    /**
     * 签名混淆key
     */
    private String tokenKey;
    /**
     * aes密钥
     */
    private Key aesKey;
    /**
     * 超时时间，毫秒
     */
//    private Long overtimeMills = 5000L;
    private Long overtimeMills = 10 * 60 * 1000L;

    public SecretInteractiveChannel() {
        this.channle = new JsonChannel();
    }

    public SecretInteractiveChannel(Socket socket) throws IOException {
        this.channle = new JsonChannel(socket);
    }

    @Override
    public InteractiveModel read() {
        JSONObject read = channle.read();
        SecretInteractiveModel secretInteractiveModel = read.toJavaObject(SecretInteractiveModel.class);

//        //  log.info("请求时长：" + formatTime(Math.abs(System.currentTimeMillis() - secretInteractiveModel.getTimestamp())));
//        log.info("超时时间：" + formatTime(overtimeMills));
        if (Math.abs(System.currentTimeMillis() - secretInteractiveModel.getTimestamp()) > overtimeMills) {
            throw new IllegalStateException("超时");
        }
        boolean checkAutograph = secretInteractiveModel.checkAutograph(tokenKey);
        if (!checkAutograph) {
            throw new IllegalStateException("签名错误");
        }
        secretInteractiveModel.decryptMsg(aesKey);

        return secretInteractiveModel;
    }

    @Override
    public void write(InteractiveModel value) {
        SecretInteractiveModel secretInteractiveModel = new SecretInteractiveModel(value);
        secretInteractiveModel.setCharset(this.getCharset().name());
        try {
            secretInteractiveModel.fullMessage(aesKey, tokenKey);
        } catch (Exception e) {
            e.printStackTrace();
        }
        channle.write(secretInteractiveModel);
    }

    @Override
    public void flush() {
        channle.flush();
    }

    @Override
    public void writeAndFlush(InteractiveModel value) {
        this.write(value);
        this.flush();
    }

    /**
     * 获取charset
     * 
     * @author Pluto
     * @since 2020-01-08 16:18:58
     * @return
     */
    public Charset getCharset() {
        return channle.getCharset();
    }

    @Override
    public void setCharset(Charset charset) {
        channle.setCharset(charset);
    }

    @Override
    public Socket getSocket() {
        return channle.getSocket();
    }

    @Override
    public void setSocket(Socket socket) throws IOException {
        channle.setSocket(socket);
    }

    @Override
    public void closeSocket() throws IOException {
        channle.closeSocket();
    }

    /**
     * 使用base64格式设置aes密钥
     * 
     * @author Pluto
     * @since 2020-01-08 16:19:07
     * @param aesKey
     */
    public void setBaseAesKey(String aesKey) {
        this.aesKey = AESUtil.createKeyByBase64(aesKey);
    }

    /*
     * 毫秒转化成时长
     */
    public static String formatTime(long ms) {

        int ss = 1000;
        int mi = ss * 60;
        int hh = mi * 60;
        int dd = hh * 24;

        long day = ms / dd;
        long hour = (ms - day * dd) / hh;
        long minute = (ms - day * dd - hour * hh) / mi;
        long second = (ms - day * dd - hour * hh - minute * mi) / ss;
        long milliSecond = ms - day * dd - hour * hh - minute * mi - second * ss;

        String strDay = day < 10 ? "0" + day : "" + day; //天
        String strHour = hour < 10 ? "0" + hour : "" + hour;//小时
        String strMinute = minute < 10 ? "0" + minute : "" + minute;//分钟
        String strSecond = second < 10 ? "0" + second : "" + second;//秒
        String strMilliSecond = milliSecond < 10 ? "0" + milliSecond : "" + milliSecond;//毫秒
//        strMilliSecond = milliSecond < 100 ? "0" + strMilliSecond : "" + strMilliSecond;

        return strDay + "天" + strHour + "时" + strMinute + "分钟 " + strSecond + "秒";
    }
}
